import React, { useRef, useState } from 'react';
import axios from 'axios';
import UploadList from './UploadList';
import Dragger from './Dragger';
// import Button from '../Button';

export type UploadFileStatus = 'ready' | 'uploading' | 'success' | 'error';

export interface UploadFile {
  uid: string;
  size: number;
  name: string;
  status?: UploadFileStatus;
  percent?: number;
  raw?: File;
  response?: any;
  error?: any;
}

export interface UploadProps {
  // 上传文件的url地址
  action: string;

  // 默认上传的文件列表
  defaultFileList?: UploadFile[];

  // 自定义 http 请求头
  headers?: { [key: string]: any };

  // 文件上传的key值，key:file
  name?: string;

  // 是否携带更多 http post 参数
  data?: { [key: string]: any };

  // 是否携带 cookie
  withCredentials?: boolean;

  // 接收的文件类型
  accept?: string;

  // 是否可以多选
  multiple?: boolean;

  // 是否可以拖拽上传
  drag?: boolean;

  // 上传之前调用，返回 false 不会继续上传
  beforeUpload?: (file: File) => boolean | Promise<File>;

  // 返回文件上传进度状态
  onProgress?: (percentage: number, file: UploadFile) => void;
  onChange?: (file: UploadFile) => void;
  onSuccess?: (data: any, file: UploadFile) => void;
  onError?: (err: any, file: UploadFile) => void;
  onRemove?: (file: UploadFile) => void;
}

interface UploadType extends React.FC<UploadProps> {}

const Upload: UploadType = ({
  action,
  defaultFileList,
  name,
  data,
  withCredentials,
  headers,
  accept,
  multiple,
  drag,
  children,
  onError,
  onProgress,
  onRemove,
  ...props
}) => {
  const fileInputRef = useRef<HTMLInputElement>(null);

  // UploadFile[] | Array<UploadFile>
  // 文件列表，用来存储文件状态来更新视图
  const [fileList, setFileList] = useState<UploadFile[]>(defaultFileList || []);

  // 更新文件列表中某一项的值
  const updateFileList = (
    updateFile: UploadFile,
    updateObj: Partial<UploadFile>
  ) => {
    setFileList(prevList => {
      return prevList.map(file => {
        if (file.uid === updateFile.uid) {
          return { ...file, ...updateObj };
        } else {
          return file;
        }
      });
    });
  };

  // 点击后唤起 input
  const handleClick = () => {
    if (fileInputRef.current) {
      fileInputRef.current.click();
    }
  };

  // input change事件，选中文件
  const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const files = e.target.files;
    if (!files) return;
    uploadFiles(files);
    if (fileInputRef.current) {
      fileInputRef.current.value = '';
    }
  };

  // 删除文件列表中的文件
  const handleRemove = (file: UploadFile) => {
    setFileList(prevList => {
      return prevList.filter(item => item.uid !== file.uid);
    });
    if (onRemove) {
      onRemove(file);
    }
  };

  // 上传选中文件
  const uploadFiles = (files: FileList) => {
    let postFiles = Array.from(files);
    postFiles.forEach(file => {
      if (!props.beforeUpload) {
        post(file);
      } else {
        const result = props.beforeUpload(file);
        if (result && result instanceof Promise) {
          result.then(processedFile => {
            post(processedFile);
          });
        } else if (result !== false) {
          post(file);
        }
      }
    });
  };

  // post 单个文件对象
  const post = (file: File) => {
    // 构建单个文件对象
    let _file: UploadFile = {
      uid: Date.now() + `-upload-file-${file.name}`,
      status: 'ready',
      name: file.name,
      size: file.size,
      percent: 0,
      raw: file,
    };

    // 更新文件列表
    setFileList(prevList => {
      return [_file, ...prevList];
    });

    // 新建表单对象
    const formData = new FormData();

    // 表单对象增加参数
    formData.append(name || 'file', file);

    // 存在其他 post 参数
    if (data) {
      Object.keys(data).forEach(key => {
        formData.append(key, data[key]);
      });
    }

    // 上传文件
    axios
      .post(action, formData, {
        headers: {
          'Content-Type': 'multipart/form-data',
          // 传入用户自定义的 headers
          ...headers,
        },
        // 是否携带 cookie
        withCredentials,
        // 上传实时更新进度
        onUploadProgress: e => {
          let percentage = Math.round((e.loaded * 100) / e.total) || 0;
          if (percentage < 100) {
            // 更新 file 列表，对应 dom 实时更新进度
            updateFileList(_file, {
              percent: percentage,
              status: 'uploading',
            });
            if (onProgress) {
              onProgress(percentage, _file);
            }
          }
        },
      })
      .then(resp => {
        // 更新file列表，对应实时更新 dom
        updateFileList(_file, {
          status: 'success',
          response: resp.data,
        });

        // 上传成功的回调
        if (props.onSuccess) {
          props.onSuccess(resp.data, _file);
        }

        // 每次上传成功或者失败都会触发 change 事件
        if (props.onChange) {
          props.onChange(_file);
        }
      })
      .catch(err => {
        // 上传失败更新 file 列表，更新dom状态
        updateFileList(_file, {
          status: 'error',
          error: err,
        });

        // 上传失败触发 error 事件
        if (onError) {
          onError(err, _file);
        }

        // 每次上传成功或者失败都会触发 change 事件
        if (props.onChange) {
          props.onChange(_file);
        }
      });
  };

  return (
    <div className='upload-component'>
      <div
        className='upload-input'
        style={{ display: 'inline-block' }}
        onClick={handleClick}
      >
        {drag ? (
          <Dragger onFile={files => uploadFiles(files)}>{children}</Dragger>
        ) : (
          children
        )}
        <input
          ref={fileInputRef}
          accept={accept}
          multiple={multiple}
          onChange={handleFileChange}
          type='file'
          style={{ display: 'none' }}
          className='upload-file-input'
        />
        <UploadList fileList={fileList} onRemove={handleRemove} />
      </div>
    </div>
  );
};

Upload.defaultProps = {
  name: 'file',
};

Upload.displayName = 'Upload';
export default Upload;
